package com.atguigu.gmall.product.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.list.client.ListFeignClient;
import com.atguigu.gmall.model.product.*;
import com.atguigu.gmall.product.mapper.*;
import com.atguigu.gmall.product.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static java.util.concurrent.TimeUnit.SECONDS;

@Service
public class SkuManageServiceImpl implements SkuManageService {
    @Autowired
    private SkuInfoService skuInfoService;
    @Autowired
    private SkuImageService skuImageService;
    @Autowired
    private SkuAttrValueService skuAttrValueService;
    @Autowired
    private SkuSaleAttrValueService skuSaleAttrValueService;
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private BaseAttrValueService baseAttrValueService;
    @Autowired
    private SpuSaleAttrValueService spuSaleAttrValueService;
    @Autowired
    private BaseAttrInfoMapper baseAttrInfoMapper;
    @Autowired
    private SpuPosterService spuPosterService;
    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;
    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;
    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ListFeignClient listFeignClient;
    @Autowired
    private BaseTrademarkService baseTrademarkService;
    @Autowired
    private RabbitService rabbitService;

    /**
     * 保存或修改SkuInfo
     *
     * @param skuInfo
     */
    @Override
    public void saveSkuInfo(SkuInfo skuInfo) {
        if (skuInfo.getId() != null) {
            skuImageService.remove(new LambdaQueryWrapper<SkuImage>().eq(SkuImage::getSkuId, skuInfo.getId()));
            skuAttrValueService.remove(new LambdaQueryWrapper<SkuAttrValue>().eq(SkuAttrValue::getSkuId, skuInfo.getId()));
            skuSaleAttrValueService.remove(new LambdaQueryWrapper<SkuSaleAttrValue>().eq(SkuSaleAttrValue::getSkuId, skuInfo.getId()));
            skuInfoService.updateById(skuInfo);
        } else {
            skuInfoService.save(skuInfo);
        }
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if (!CollectionUtils.isEmpty(skuImageList)) {
            skuImageList.forEach(skuImage -> skuImage.setSkuId(skuInfo.getId()));
            skuImageService.saveBatch(skuImageList);
        }
        List<SkuAttrValue> skuAttrValueList = skuInfo.getSkuAttrValueList();
        if (!CollectionUtils.isEmpty(skuAttrValueList)) {
            skuAttrValueList.forEach(skuAttrValue -> skuAttrValue.setSkuId(skuInfo.getId()));
            skuAttrValueService.saveBatch(skuAttrValueList);
        }
        List<SkuSaleAttrValue> skuSaleAttrValueList = skuInfo.getSkuSaleAttrValueList();
        if (!CollectionUtils.isEmpty(skuSaleAttrValueList)) {
            skuSaleAttrValueList.forEach(skuSaleAttrValue -> {
                skuSaleAttrValue.setSkuId(skuInfo.getId());
                skuSaleAttrValue.setSpuId(skuInfo.getSpuId());
            });
            skuSaleAttrValueService.saveBatch(skuSaleAttrValueList);
        }
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);
        bloomFilter.add(skuInfo.getId());
    }

    /**
     * sku分页列表
     *
     * @param pageInfo
     * @param category3Id
     * @return
     */
    @Override
    public IPage<SkuInfo> getSkuInfoList(Page<SkuInfo> pageInfo, Long category3Id) {
        LambdaQueryWrapper<SkuInfo> spuInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        spuInfoLambdaQueryWrapper.eq(SkuInfo::getCategory3Id, category3Id).orderByDesc(SkuInfo::getId);
        Page<SkuInfo> skuInfoPage = skuInfoMapper.selectPage(pageInfo, spuInfoLambdaQueryWrapper);
        return skuInfoPage;
    }

    /**
     * 上架
     *
     * @param skuId
     */
    @Override
    public void onSale(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (skuInfo != null || skuInfo.getIsSale() != 1) {
            String redisKey = "sku:" + skuId + ":info";
            redisTemplate.delete(redisKey);
            skuInfo.setIsSale(1);
            skuInfoMapper.updateById(skuInfo);

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            redisTemplate.delete(redisKey);
            //发送消息
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS, MqConst.ROUTING_GOODS_UPPER, skuId);
        }
    }

    /**
     * 下架
     *
     * @param skuId
     */
    @Override
    public void cancelSale(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (skuInfo != null || skuInfo.getIsSale() != 0) {
            String redisKey = "sku:" + skuId + ":info";
            redisTemplate.delete(redisKey);
            skuInfo.setIsSale(0);
            skuInfoMapper.updateById(skuInfo);

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            redisTemplate.delete(redisKey);
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_GOODS, MqConst.ROUTING_GOODS_LOWER, skuId);
        }
    }

    /**
     * 根据skuId查询Sku
     *
     * @param skuId
     * @return
     */
    @Override
    @GmallCache(prefix = "sku:")
    public SkuInfo getSkuInfo(Long skuId) {
        return this.getSkuInfoByDB(skuId);
    }

    private SkuInfo getSkuInfoByRedission(Long skuId) {
        try {
            String skuKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
            SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
            if (skuInfo == null) {
                //redission--key
                String skuLocKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
                //创建分布式锁
                RLock lock = redissonClient.getLock(skuLocKey);
                //上锁
                lock.lock();
                try {
                    //缓存中没有去数据库中查询
                    skuInfo = this.getSkuInfoByDB(skuId);
                    if (skuInfo == null) {
                        //如果数据库没有数据，返回空对象，并设置过期时间
                        SkuInfo skuInfo1 = new SkuInfo();
                        redisTemplate.opsForValue().set(skuKey, skuInfo1, 10, TimeUnit.MINUTES);
                        return skuInfo1;
                    }
                    redisTemplate.opsForValue().set(skuKey, skuInfo);
                    return skuInfo;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            } else {
                return skuInfo;
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        return this.getSkuInfo(skuId);
    }

    private SkuInfo getSkuInfoByRedis(Long skuId) {
        try {
            //拼接Key
            String skuKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
            //先去缓存中查询数据
            SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
            if (skuInfo == null) {
                //缓存中没有去数据库中查询
                //先添加分布式锁的key
                //创建锁的key名
                String locKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;

                String uuid = UUID.randomUUID().toString();
                //创建锁
                Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 10, SECONDS);
                if (ifAbsent) {
                    skuInfo = this.getSkuInfoByDB(skuId);
                    if (skuInfo == null) {
                        //如果数据库没有数据，返回空对象，并设置过期时间
                        SkuInfo skuInfo1 = new SkuInfo();
                        redisTemplate.opsForValue().set(skuKey, skuInfo1, 10, TimeUnit.MINUTES);
                        return skuInfo1;
                    }
                    redisTemplate.opsForValue().set(skuKey, skuInfo);
                    //释放资源
                    expireKey(locKey, uuid);

                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    this.getSkuInfo(skuId);
                }

                return skuInfo;
            } else {
                skuInfo = getSkuInfoByDB(skuId);
                return skuInfo;
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        return this.getSkuInfo(skuId);

    }

    private void expireKey(String locKey, String uuid) {
        //  释放锁资源：
        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //释放锁
        //设置lua脚本
        DefaultRedisScript redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(scriptText);
        redisScript.setResultType(Long.class);
        redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid);
    }

    private SkuInfo getSkuInfoByDB(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (skuInfo != null) {
            List<SkuImage> skuImageList = skuImageService.list(new LambdaQueryWrapper<SkuImage>().eq(SkuImage::getSkuId, skuInfo.getId()));
            if (!CollectionUtils.isEmpty(skuImageList)) {
                skuInfo.setSkuImageList(skuImageList);
            }
//            List<SkuAttrValue> skuAttrValueList = skuAttrValueService.list(new LambdaQueryWrapper<SkuAttrValue>().eq(SkuAttrValue::getSkuId, skuInfo.getId()));
//            if (!CollectionUtils.isEmpty(skuAttrValueList)) {
//                skuAttrValueList.forEach(skuAttrValue -> {
//                    skuAttrValue.setValueName(baseAttrValueService.getById(skuAttrValue.getValueId()).getValueName());
//                    skuAttrValue.setAttrName(baseAttrInfoMapper.selectById(skuAttrValue.getAttrId()).getAttrName());
//                });
//                skuInfo.setSkuAttrValueList(skuAttrValueList);
//            }
//            List<SkuSaleAttrValue> skuSaleAttrValueList = skuSaleAttrValueService.list(new LambdaQueryWrapper<SkuSaleAttrValue>().eq(SkuSaleAttrValue::getSkuId, skuInfo.getId()));
//            if (!CollectionUtils.isEmpty(skuSaleAttrValueList)) {
//                skuSaleAttrValueList.forEach(skuSaleAttrValue -> {
//                    SpuSaleAttrValue spuSaleAttrValue = spuSaleAttrValueService.getById(skuSaleAttrValue.getSaleAttrValueId());
//                    skuSaleAttrValue.setSaleAttrValueName(spuSaleAttrValue.getSaleAttrValueName());
//                    skuSaleAttrValue.setBaseSaleAttrId(spuSaleAttrValue.getBaseSaleAttrId());
//                    skuSaleAttrValue.setSaleAttrName(spuSaleAttrValue.getSaleAttrName());
//                });
//                skuInfo.setSkuSaleAttrValueList(skuSaleAttrValueList);
//            }
        }
        return skuInfo;
    }

    /**
     * @param spuId
     * @return
     */
    @Override
    @GmallCache(prefix = "spuPosterBySpuId")
    public List<SpuPoster> getSpuPosterBySpuId(Long spuId) {
        return spuPosterService.list(new LambdaQueryWrapper<SpuPoster>().eq(SpuPoster::getSpuId, spuId));
    }

    /**
     * 根据三级分类id获取分类信息
     *
     * @param category3Id
     * @return
     */

    @Override
    @GmallCache(prefix = "categoryView")
    public BaseCategoryView getCategoryView(Long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }

    /**
     * 根据skuId 获取最新的商品价格
     *
     * @param skuId
     * @return
     */
    @Override
    public BigDecimal getSkuPrice(Long skuId) {
        String locKey = skuId + ":lock";
        RLock lock = redissonClient.getLock(locKey);
        lock.lock();
        try {
            LambdaQueryWrapper<SkuInfo> skuInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            skuInfoLambdaQueryWrapper.eq(SkuInfo::getId, skuId).select(SkuInfo::getPrice);
            SkuInfo skuInfo = skuInfoMapper.selectOne(skuInfoLambdaQueryWrapper);
            if (skuInfo != null) {
                return skuInfo.getPrice();
            }
            return new BigDecimal("0");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 根据skuId 获取平台属性数据
     *
     * @param skuId
     * @return
     */
    @Override
    @GmallCache(prefix = "attrList")
    public List<BaseAttrInfo> getAttrList(Long skuId) {
        List<BaseAttrInfo> baseAttrInfoList = baseAttrInfoMapper.getSkuAttrList(skuId);
        return baseAttrInfoList;
    }

    /**
     * 根据spuId,skuId 获取销售属性数据
     *
     * @param spuId
     * @param skuId
     * @return
     */
    @Override
    @GmallCache(prefix = "spuSaleAttrList")
    public List<SpuSaleAttr> getSpuSaleAttrList(Long spuId, Long skuId) {
        return spuSaleAttrMapper.getSpuSaleAttrListBySpuIdAndSkuId(spuId, skuId);
    }

    /**
     * 根据spuId 获取到销售属性值Id 与skuId 组成的数据集
     *
     * @param spuId
     * @return
     */
    @Override
    @GmallCache(prefix = "skuValueIdsMap")
    public Map<Object, Object> getSkuValueIdsMap(Long spuId) {
        HashMap<Object, Object> map = new HashMap<>();
        List<Map<Object, Object>> mapList = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
        mapList.forEach(stringMap -> {
            System.out.println("stringMap = " + stringMap);
            map.put(stringMap.get("values_ids"), stringMap.get("sku_id"));
        });

        return map;
    }

    /**
     * 获取分类集合
     *
     * @return
     */
    @Override
    public List<JSONObject> getBaseCategoryList() {
        //创建空集合
        ArrayList<JSONObject> list = new ArrayList<>();
        //获取全部分类数据
        List<BaseCategoryView> baseCategoryViewList = baseCategoryViewMapper.selectList(null);
        //根据category1Id进行分组获得map  category1
        Map<Long, List<BaseCategoryView>> category1Map = baseCategoryViewList.stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory1Id));
        //获取迭代器
        Iterator<Map.Entry<Long, List<BaseCategoryView>>> iterator1 = category1Map.entrySet().iterator();

        int index = 1;
        //遍历category1Map
        while (iterator1.hasNext()) {
            Map.Entry<Long, List<BaseCategoryView>> entry1 = iterator1.next();
            Long category1Id = entry1.getKey();
            List<BaseCategoryView> category1ViewList = entry1.getValue();
            //根据1级分类Id,封装成jsonObject
            JSONObject category1 = new JSONObject();
            category1.put("index", index);
            index++;
            category1.put("categoryName", category1ViewList.get(0).getCategory1Name());
            category1.put("categoryId", category1Id);
            //再根据category2Id进行分组
            Map<Long, List<BaseCategoryView>> category2Map = category1ViewList.stream().collect(Collectors.groupingBy(BaseCategoryView::getCategory2Id));
            Iterator<Map.Entry<Long, List<BaseCategoryView>>> iterator2 = category2Map.entrySet().iterator();
            ArrayList<JSONObject> category2List = new ArrayList<>();
            while (iterator2.hasNext()) {
                Map.Entry<Long, List<BaseCategoryView>> entry2 = iterator2.next();
                Long category2Id = entry2.getKey();
                List<BaseCategoryView> baseCategory2Views = entry2.getValue();
                JSONObject category2 = new JSONObject();
                category2.put("categoryName", baseCategory2Views.get(0).getCategory2Name());
                category2.put("categoryId", category2Id);
                //创建三级分类Json数据
                ArrayList<JSONObject> category3List = new ArrayList<>();
                baseCategory2Views.forEach(baseCategoryView -> {
                    JSONObject category3 = new JSONObject();
                    category3.put("categoryName", baseCategoryView.getCategory3Name());
                    category3.put("categoryId", baseCategoryView.getCategory3Id());
                    category3List.add(category3);
                });
                //存入二级分类的categoryChild中
                category2.put("categoryChild", category3List);
                category2List.add(category2);
            }
            //存入一级分类的categoryChild中
            category1.put("categoryChild", category2List);
            list.add(category1);
        }

        return list;
    }

    @Override
    public BaseTrademark getTrademark(Long tmId) {
        return baseTrademarkService.getById(tmId);

    }


}
